package com.zhichat.infrastructure.interceptor;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.zhichat.infrastructure.config.ZhiChatConfig;
import com.zhichat.infrastructure.config.ZhiChatGlobalUtil;
import com.zhichat.infrastructure.constants.Constants;
import com.zhichat.infrastructure.exception.http.CommonException;
import com.zhichat.infrastructure.redis.Keys;
import com.zhichat.infrastructure.redis.RedisRepository;
import com.zhichat.infrastructure.redis.rediskey.Key;
import com.zhichat.infrastructure.redis.rediskey.ZhiChat;
import com.zhichat.infrastructure.util.ThreadLocalCache;
import com.zhichat.web.api.R;
import com.zhichat.web.dto.UserToken;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.UrlPathHelper;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * Server 鉴权
 *
 * @author hayabusa
 */
@Slf4j
@Getter
@Component
@AllArgsConstructor
public final class ServerAccessInterceptor implements HandlerInterceptor, InitializingBean {

    private final UrlPathHelper urlPathHelper = new UrlPathHelper();
    private final PathMatcher pathMatcher = new AntPathMatcher();
    private final List<String> excludePatterns = Lists.newCopyOnWriteArrayList();
    private final RedisRepository redisRepository;
    private final ZhiChatConfig zhiChatConfig;
    private final ZhiChatGlobalUtil zhiChatGlobalUtil;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("ServletPath - > {}", request.getServletPath());
        String lookupPath = urlPathHelper.getLookupPathForRequest(request);
//        log.info("lookupPath - > {}", lookupPath);
        // 白名单
        for (String pattern : this.excludePatterns) {
            if (pathMatcher.match(pattern, lookupPath)) {
                return true;
            }
        }
        // 鉴权
        String token = request.getHeader(Constants.AUTHORIZATION);
        if (!StringUtils.hasText(token) || !zhiChatGlobalUtil.verifyToken(token) || Objects.isNull(zhiChatGlobalUtil.getPayload(token))) {
            throw CommonException.UNLOGIN_COMMON_EXCEPTION;
        }
        Long userId = zhiChatGlobalUtil.getPayload(token).getLong("uId");
        if (Objects.isNull(userId) || 0L == userId) {
            throw CommonException.UNLOGIN_COMMON_EXCEPTION;
        }
        // 获取用户信息
        Key tokenKey = new ZhiChat(Keys.TOKEN);
        UserToken userToken = redisRepository.get(tokenKey, UserToken.class, userId);
        if (Objects.isNull(userToken) || !Objects.equals(userId, userToken.getUserId()) || !Objects.equals(token, userToken.getToken())) {
            throw CommonException.UNLOGIN_COMMON_EXCEPTION;
        }
        // 校验 是否是管理员
        if (lookupPath.startsWith("/admin") && !userToken.getIsAdmin()) {
            throw CommonException.UNPERMISSION_COMMON_EXCEPTION;
        }
        // 放入 ThreadLocal 内
        ThreadLocalCache.putUserToken(userToken);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        ThreadLocalCache.clear();
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }

    @SuppressWarnings("deprecation")
    public static void redenrJSON(HttpServletRequest request, HttpServletResponse response, String message) throws IOException {
        response.setHeader("content-type", MediaType.APPLICATION_JSON_UTF8_VALUE);
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.getWriter().write(JSONObject.toJSONString(R.fail("先等等，我想想")));
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        String excluds = zhiChatConfig.getExclude();
        if (StringUtils.hasText(excluds)) {
            excludePatterns.addAll(Arrays.asList(excluds.split(",")));
        }
        log.info("not check url：{}", excludePatterns.toString());
    }
}
